/*
** Copyright (C) 2010 Calin Radoni
**
** This file is part of CHScanner.
**
** CHScanner is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** CHScanner is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with CHScanner. If not, see <http://www.gnu.org/licenses/>.
*/

#include "StdAfx.h"
#include "MAddressIPv6.h"

#include <ws2tcpip.h>

//--------------------------------------------------------------------

MAddressIPv6::MAddressIPv6(void)
{
	Initialize();
}

MAddressIPv6::~MAddressIPv6(void)
{
}

//--------------------------------------------------------------------

void MAddressIPv6::Initialize(void)
{
	memset(ia.u.Byte, 0, 16);
}


void MAddressIPv6::LoadFromPointer(MAddressIPv6* addrIn)
{
	Initialize();
	if(addrIn==NULL) return;

	memcpy(ia.u.Byte, addrIn->ia.u.Byte, 16);
}

//--------------------------------------------------------------------

bool MAddressIPv6::IsEmpty(void)
{
	if(ia.u.Word[0]!=0) return false;
	if(ia.u.Word[1]!=0) return false;
	if(ia.u.Word[2]!=0) return false;
	if(ia.u.Word[3]!=0) return false;
	if(ia.u.Word[4]!=0) return false;
	if(ia.u.Word[5]!=0) return false;
	if(ia.u.Word[6]!=0) return false;
	if(ia.u.Word[7]!=0) return false;

	return true;
}

bool MAddressIPv6::IsEqualWith(in_addr6* ia6In)
{
	bool isEqual;
	int i;

	if(ia6In==NULL) return false;

	isEqual = true;
	for(i=0; i<8 && isEqual; i++){
		if(ia.u.Word[i] != ia6In->u.Word[i])
			isEqual = false;
	}

	return isEqual;
}

//--------------------------------------------------------------------

void MAddressIPv6::Set(in_addr6* addrIn)
{
	Initialize();
	if(addrIn==NULL) return;

	memcpy(ia.u.Byte, addrIn->u.Byte, 16);
}

bool MAddressIPv6::SetS(CString strIn)
{
	addrinfo *addrInfo;
	addrinfo aiHint;
	sockaddr_in6 *sa6;
	int res;

	memset(&aiHint, 0, sizeof(addrinfo));
	aiHint.ai_family = PF_INET6;
	res = getaddrinfo(strIn, NULL, &aiHint, &addrInfo);
	if(res!=0) return false;

	if(addrInfo==NULL) return false;
	if(addrInfo->ai_addr==NULL){
		freeaddrinfo(addrInfo);
		return false;
	}
	if(addrInfo->ai_addr->sa_family!=PF_INET6){
		freeaddrinfo(addrInfo);
		return false;
	}

	sa6 = (sockaddr_in6*)addrInfo->ai_addr;
	memcpy(&ia, &(sa6->sin6_addr), sizeof(in_addr6));
	
	freeaddrinfo(addrInfo);

	return true;
}

//--------------------------------------------------------------------

void MAddressIPv6::Get(in_addr6* addrIn)
{
	if(addrIn==NULL) return;

	memcpy(addrIn->u.Byte, ia.u.Byte, 16);
}

CString MAddressIPv6::GetS(void)
{
	CString str;
	sockaddr_in6 sa6;
	int res;
	TCHAR hostName[NI_MAXHOST];

	str.Empty();

	memset(hostName, 0, NI_MAXHOST*sizeof(TCHAR));
	memset(&sa6, 0, sizeof(sockaddr_in6));
	sa6.sin6_family = PF_INET6;
	memcpy(&sa6.sin6_addr, &ia, sizeof(in_addr6));
	res = getnameinfo((sockaddr*)&sa6, sizeof(sockaddr_in6), hostName, NI_MAXHOST*sizeof(TCHAR), NULL, 0, NI_NUMERICHOST);
	if(res!=0) return str;

	str = hostName;

	return str;
}

//--------------------------------------------------------------------
/*
RFC 4291: 2.5.6.  Link-Local IPv6 Unicast Addresses

   Link-Local addresses are for use on a single link.  Link-Local
   addresses have the following format:

   |   10     |
   |  bits    |         54 bits         |          64 bits           |
   +----------+-------------------------+----------------------------+
   |1111111010|           0             |       interface ID         |
   +----------+-------------------------+----------------------------+

   Link-Local addresses are designed to be used for addressing on a
   single link for purposes such as automatic address configuration,
   neighbor discovery, or when no routers are present.

   Routers must not forward any packets with Link-Local source or
   destination addresses to other links.
*/
bool MAddressIPv6::IsLinkLocalUnicastAddress(void)
{
	BYTE bbb;

	if(ia.u.Byte[0]!=0xFE)
		return false;

	bbb = ia.u.Byte[1] & 0xC0;
	if(bbb!=0x80)
		return false;

	return true;
}

/* RFC 4291: 2.7.  Multicast Addresses

   An IPv6 multicast address is an identifier for a group of interfaces
   (typically on different nodes).  An interface may belong to any
   number of multicast groups.  Multicast addresses have the following
   format:

   |   8    |  4 |  4 |                  112 bits                   |
   +------ -+----+----+---------------------------------------------+
   |11111111|flgs|scop|                  group ID                   |
   +--------+----+----+---------------------------------------------+
*/
bool MAddressIPv6::IsMulticastAddress(void)
{
	return ia.u.Byte[0]==0xFF ? true : false;
}

//--------------------------------------------------------------------

bool MAddressIPv6::CompareLastThreeMulticastBytes(in_addr6* iaIn)
{
	if(iaIn==NULL) return false;

	if(ia.u.Byte[13]!=iaIn->u.Byte[13]) return false;
	if(ia.u.Byte[14]!=iaIn->u.Byte[14]) return false;
	if(ia.u.Byte[15]!=iaIn->u.Byte[15]) return false;

	return true;
}

void MAddressIPv6::GetLastThreeMulticastBytes(in_addr6* iaIn)
{
	if(iaIn==NULL) return;

	iaIn->u.Byte[13] = ia.u.Byte[13];
	iaIn->u.Byte[14] = ia.u.Byte[14];
	iaIn->u.Byte[15] = ia.u.Byte[15];
}

//--------------------------------------------------------------------
